// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "system.h"
#include "filehdr.h"

//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------

bool
FileHeader::Allocate(BitMap *freeMap, int fileSize)
{ 
    numBytes = fileSize;
    numSectors  = divRoundUp(fileSize, SectorSize);
    if (freeMap->NumClear() < numSectors)
	return FALSE;		// not enough space
    DEBUG('a', "FileHeader::Allocate numSectors: %d\n",numSectors);
    // 有额外的空间
    if(numSectors <= NumDirect){
        //直接块能放的下
        DEBUG('a', "FileHeader::Allocate create directIndex\n");
        for (int i = 0; i < numSectors; i++)
	    dataSectors[i] = freeMap->Find();
    }else if(numSectors <= (NumDirect + NumPerSector)){
        // 一级间接块能放下
        DEBUG('a', "FileHeader::Allocate create singleIndex\n");
        int i = 0;
        for (; i < NumDirect; i++)
	    dataSectors[i] = freeMap->Find();
        // 指定间接块的块号
        singleIndirect = freeMap->Find();
        int singleSectors[NumPerSector];
        // 为一级间接块分配空闲块
        int k = 0;
        for(;i < numSectors;i++)
            singleSectors[k++] = freeMap->Find();
        // 将一级间接块写回
        synchDisk->WriteSector(singleIndirect, (char *)singleSectors); 
    }else{
        // 需要用到二级间接块
        DEBUG('a', "FileHeader::Allocate create doubleIndex\n");
        for (int i=0; i < NumDirect; i++){
	    dataSectors[i] = freeMap->Find();
        DEBUG('a', "FileHeader::Allocate directSector %d\n",dataSectors[i]);
        }
        // 指定间接块的块号
        
        singleIndirect = freeMap->Find();
        int singleSectors[NumPerSector];
        // 为一级间接块分配空闲块
        for(int i = 0;i < NumPerSector;i++){
            singleSectors[i] = freeMap->Find();
            DEBUG('a', "FileHeader::Allocate singleSector i: %d  %d\n",i,singleSectors[i]);
        }
        // 将一级间接块写回
        synchDisk->WriteSector(singleIndirect, (char *)singleSectors); 
        
        int doubleDataSecotrs[NumPerSector][NumPerSector];
        int doubleIndexSectors[NumPerSector];
        doubleIndirect = freeMap->Find();
        DEBUG('a', "FileHeader::Allocate doubleIndirect %d\n",doubleIndirect);
        // 二级索引中有多少数据块
        int dataSectorNum = numSectors - NumDirect - NumPerSector;
        // 需要多少索引块
        int indexSectorNum = divRoundUp(dataSectorNum, NumPerSector);
        DEBUG('a', "FileHeader::Allocate dataSectorNum :%d indexSectorNum %d\n",dataSectorNum,indexSectorNum);
        int count = 0;
        for(int k = 0;k < indexSectorNum;k++){
            doubleIndexSectors[k] = freeMap->Find();
            for(int j = 0;j < NumPerSector;j++){
                doubleDataSecotrs[k][j] = freeMap->Find();
                count++;
                if(count == dataSectorNum)
                    break;
                DEBUG('a', "FileHeader::Allocate count :%d doubledataSector %d\n",count,doubleDataSecotrs[k][j]);
            }
            // 将二级索引块写回磁盘
            synchDisk->WriteSector(doubleIndexSectors[k], (char *)doubleDataSecotrs[k]);
        }
        // 将一级间接块写回
        synchDisk->WriteSector(doubleIndirect,(char *)doubleIndexSectors);

    }

    return TRUE;
}


//----------------------------------------------------------------------
// FileHeader::Reallocate
// 	根据当前文档的大小，和文件增长大小，为该文件分配更多的块数。
//----------------------------------------------------------------------
bool FileHeader::Reallocate(BitMap *freeMap,int fileSize,int incrementBytes){
    
    //总字节数
    int totalBytes = fileSize + incrementBytes;

    // 当前块  已经处理完的块数
    int currentSector = numSectors;
    // 计算新增块数 
    int increSectors = divRoundUp(totalBytes,SectorSize) - numSectors;
    DEBUG('a', "FileHeader::Reallocate fileSize: %d incrementBytes %d  numSectors: %d increSectors: %d\n",
    fileSize,incrementBytes,numSectors,increSectors);
    // 判断是否有足够的剩余块
    if(freeMap->NumClear() < increSectors)
        return FALSE;
    // 为文件分配剩余块
    // 记录以分配多少块
    int totalSector = numSectors + increSectors;
    // 在每一个部分增长的数量
    int directSize = -1;
    int singleSize = -1;
    int doubleSize = -1;
    
    // 计算需要多少个直接块 结果为 负 也不会分配直接块
    if(totalSector > NumDirect)
        directSize = NumDirect - numSectors;
    else 
        directSize  = totalSector - numSectors;
    
    // 计算一级索引有多少块
    if(numSectors < (NumDirect + NumPerSector) && totalSector > NumDirect){
        //需要分配一级索引
        int l = (numSectors > NumDirect) ? numSectors : NumDirect;
        int r = ((NumDirect+NumPerSector) < totalSector) ? (NumDirect+NumPerSector) : totalSector;
        singleSize = r - l; 
    }else{
        singleSize = 0;
    }
    
    // 计算二级索引 有多少块
    if(totalSector > (NumDirect + NumPerSector)){
        int l = (numSectors > (NumDirect+NumPerSector)) ? numSectors : (NumDirect+NumPerSector);
        doubleSize = totalSector - l;
    }else{
        // 不需要二级索引
        doubleSize = 0;
    }
    // 如果numsSetor > NUmDirest 不用考虑直接块
    if(numSectors <= NumDirect){
        for(int i = 0;i < directSize;i++){
            dataSectors[currentSector++] = freeMap->Find();
            DEBUG('a', "FileHeader::Reallocate  singleSectors[%d] = %d \n",
            (currentSector-1),dataSectors[currentSector-1] );
        }
    }
    DEBUG('a', "FileHeader::Reallocate directSize: %d singleSize %d doubleSize %d \n",directSize,singleSize,doubleSize);

    // 如果numsSetor > NUmDirest + NUMPerSector 不用考虑一级块
    if((numSectors <= NumDirect + NumPerSector) && totalSector > NumDirect){
        // 考虑分配一级间接块
        if(numSectors <= NumDirect){
            // 需要为 间接块分配空间
            singleIndirect = freeMap->Find();
            DEBUG('a', "FileHeader::Reallocate allocate a sector for singleIndirect\n");
        }
        int singleSectors[NumPerSector];
        // 将一级间接块读入
        synchDisk->ReadSector(singleIndirect, (char *)singleSectors);
        for(int i =0;i < singleSize;i++){
            int index =  currentSector - NumDirect;
            
            singleSectors[index] = freeMap->Find();
            DEBUG('a', "FileHeader::Reallocate currentSector %d singleSectors[%d] = %d \n",
            currentSector,index,singleSectors[index] );
            currentSector++;        
        }
        synchDisk->WriteSector(singleIndirect, (char *)singleSectors);

    }

    if(totalSector > NumDirect + NumPerSector){
        // 考虑分配二级块
        if(numSectors <=  NumDirect + NumPerSector){
            // 先为二级间接块 分配块
            doubleIndirect = freeMap->Find();
            DEBUG('a', "FileHeader::Reallocate allocate a sector for doubleIndirect\n");
        }
        // 读入一级块的内容
        int doubleIndex[NumPerSector];
        synchDisk->ReadSector(doubleIndirect,(char*)doubleIndex);
        

        int index = currentSector - NumDirect - NumPerSector;
        int count = 0;
        // 计算在二级块中的位置
        int index_col = index % NumPerSector;
        int index_row = index / NumPerSector;
        DEBUG('a', "FileHeader::Reallocate index: %d index_row %d index_col %d\n",index,index_row,index_col);

        for( ; index_row < NumPerSector;index_row++){
            // 已经处理的块数达到要求了 停止执行
            if(count >= doubleSize)
                    break;
             int doubleSectors[NumPerSector];
            if(index_col > 0){
                // 二级块的内容已经有了 里面放的是数据块的块号 从硬盘读入
                synchDisk->ReadSector(doubleIndex[index_row],(char*)doubleSectors);
            }else {
                // 还没有分配二级块 为其分配一块
                doubleIndex[index_row] = freeMap->Find();
                DEBUG('a', "FileHeader::Reallocate allocate doubleIndex %d\n",doubleIndex[index_row]);
            }
            // 向二级块中写入内容
            for( ; index_col < NumPerSector;index_col++){
                // 已经处理的块数达到要求了，停止执行
                if(count >= doubleSize)
                    break;
                doubleSectors[index_col] = freeMap->Find();
                count++;
                DEBUG('a', "FileHeader::Reallocate count %d index_row %d index_col %d sector %d\n",
                count,index_row,index_col,doubleSectors[index_col]);

            }
            // 将二级块写回磁盘
            synchDisk->WriteSector(doubleIndex[index_row],(char*)doubleSectors);
            DEBUG('a',"FileHeader::Reallocate writeback doubleSectors index_row %d \n",index_row);
        }
        // 将一级块写回磁盘
         synchDisk->WriteSector(doubleIndirect,(char*)doubleIndex);
         DEBUG('a',"FileHeader::Reallocate writeback doubleIndex\n");
                
    }
    // 每个文件最多只有 NumDirect （30）块可以用，如果超过了30块，怎么办?
    
    //更新总的块数
    numSectors = numSectors + increSectors;
    // 更新总字节数
    numBytes = fileSize + incrementBytes;
    return TRUE;
}


//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void 
FileHeader::Deallocate(BitMap *freeMap)
{
    // TODO : 适应多级索引
    // 直接块中清除
    int direct = 0;
    if(numSectors > NumDirect)
        direct = NumDirect;
    else 
        direct = numSectors;
    //清除直接块
    for (int i = 0; i < direct; i++) {
	ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
	freeMap->Clear((int) dataSectors[i]);
    }

    //清除一级块
    int single = 0;
    if(numSectors > NumDirect + NumPerSector){
        single = NumPerSector;
    }else if(numSectors > NumDirect && numSectors <= NumDirect+NumPerSector){
        single = numSectors - NumDirect;
    }

    if(single > 0){
        int singleSectors[NumPerSector];
        // 将一级块读入
        synchDisk->ReadSector(singleIndirect, (char *)singleSectors);
        for(int i = 0; i < single;i++){
            DEBUG('a', "FileHeader::Deallocate clear singleSector %d\n",singleSectors[i]);
            ASSERT(freeMap->Test((int) singleSectors[i]));
            freeMap->Clear((int)singleSectors[i]);
        }
        // 将一级块清除
        ASSERT(freeMap->Test((int) singleIndirect));
        freeMap->Clear((int)singleIndirect);
    }
    //清除二级块
    if(numSectors > NumDirect + NumPerSector){
        int doubleNum = numSectors - NumPerSector - NumDirect;

        int doubleIndex[NumPerSector];
        int doubleSectors[NumPerSector];
        // 读入二级块
        synchDisk->ReadSector(doubleIndirect, (char *)doubleIndex);
        for(int i = 0;i < NumPerSector;i++){
            if(doubleNum == 0)
                break;
            synchDisk->ReadSector(doubleIndex[i], (char *)doubleSectors);
            for(int j = 0;j < NumPerSector;j++){
                if(doubleNum == 0)
                    break;
                ASSERT(freeMap->Test((int) doubleSectors[j]));
                freeMap->Clear((int) doubleSectors[j]);
                DEBUG('a', "FileHeader::Deallocate clear doubleSector %d\n",doubleSectors[i]);
                doubleNum--;
            }
            // 将二级块清除
            ASSERT(freeMap->Test((int) doubleIndex[i]));
            freeMap->Clear((int)doubleIndex[i]);
        }
        // 将一级块清除
        ASSERT(freeMap->Test((int) doubleIndirect));
        freeMap->Clear((int)doubleIndirect);
    }
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void
FileHeader::FetchFrom(int sector)
{
    synchDisk->ReadSector(sector, (char *)this);
}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void
FileHeader::WriteBack(int sector)
{
    synchDisk->WriteSector(sector, (char *)this); 
}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int
FileHeader::ByteToSector(int offset)
{
    //return(dataSectors[offset / SectorSize]);
    // 计算出是整个文件的第几块
    int sectorOffSet = offset / SectorSize;
    int ans = 0;
    if(sectorOffSet < NumDirect){
        // 能在直接块中找到
       
        ans = dataSectors[sectorOffSet];
    } else if(sectorOffSet < (NumDirect + NumPerSector)){
        //能在一级间接块中找到
        // 读入一级间接块
       
        int singleSectors[NumPerSector];
        synchDisk->ReadSector(singleIndirect,(char *) singleSectors);
        ans = singleSectors[sectorOffSet - NumDirect];
    }else {
        
        // 要到二级间接块中寻找
        int doubleDataSecotrs[NumPerSector];
        int doubleIndexSectors[NumPerSector];
       
        synchDisk->ReadSector(doubleIndirect,(char *)doubleIndexSectors);

        // 计算 每一块中索引位置
        sectorOffSet = sectorOffSet - NumDirect - NumPerSector;
        int indexFirst = sectorOffSet / NumPerSector;
        int indexSecond = sectorOffSet % NumPerSector;
        // 读出间接块的 块号
        int sectorSecond =  doubleIndexSectors[indexFirst];
      

        // 读出数据块的块号 
        synchDisk->ReadSector(sectorSecond,(char *)doubleDataSecotrs);
        ans = doubleDataSecotrs[indexSecond];
        
    }
    return ans;
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
FileHeader::FileLength()
{
    return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.
//----------------------------------------------------------------------

void
FileHeader::Print()
{
    // 因为inode结构发生变化了所以，这里也要改
    int i, j, k;
    char *data = new char[SectorSize];
    int singleSectors[NumPerSector];
    int doubleSectors[NumPerSector][NumPerSector];
    int doubleIndex[NumPerSector];
    printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
    // 输出块号
    if(numSectors <= NumDirect){
        for (i = 0; i < numSectors; i++)
	    printf("%d ", dataSectors[i]);
    }else if(numSectors <= (NumDirect+NumPerSector)){
        // 需要一级索引
        int i = 0;
        for(;i < NumDirect;i++)
            printf("%d ", dataSectors[i]);
        synchDisk->ReadSector(singleIndirect,(char*) singleSectors);
        int k= 0;
        for(;i<numSectors;i++)
            printf("%d ",singleSectors[k++]);
    }else{
        // 需要两级索引
        for(int i = 0;i < NumDirect;i++)
            printf("%d ", dataSectors[i]);
        synchDisk->ReadSector(singleIndirect,(char*) singleSectors);    
        for(int i = 0;i < NumPerSector;i++)
            printf("%d ",singleSectors[i]);
        synchDisk->ReadSector(doubleIndirect,(char*) doubleIndex);
        // 还剩多少块
        int leftNum = numSectors - NumDirect - NumPerSector;
        int count  = 0;
        for(int i = 0;i < NumPerSector;i++){
            synchDisk->ReadSector(doubleIndex[i], (char*)doubleSectors[i]);
            for(int j = 0;j < NumPerSector;j++){
                printf("%d ",doubleSectors[i][j]);
                count++;
                if(count == leftNum)
                    break;
            }
            if(count == leftNum)
                    break;
        }
    }
    printf("\nFile contents:\n");
  
    
    k = 0;
    //输出直接块中的内容
    for(int i= 0;i < NumDirect;i++){
        synchDisk->ReadSector(dataSectors[i], data);
        for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
	    if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
		printf("%c", data[j]);
            else
		printf("\\%x", (unsigned char)data[j]);
	    }
        printf("\n"); 
        if(k == numBytes){
            delete [] data;
            return;
        }
    }
    //输出 一级间接块中的内容
    for(int i = 0;i < NumPerSector;i++){
        synchDisk->ReadSector(singleSectors[i], data);
        for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
	    if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
		printf("%c", data[j]);
            else
		printf("\\%x", (unsigned char)data[j]);
	    }
        printf("\n"); 
        if(k == numBytes){
            delete [] data;
            return;
        }
    }
    // 输出二级间接块中的内容
    for(int p = 0;p<NumPerSector;p++){
        for(int q = 0;q < NumPerSector;q++){
            synchDisk->ReadSector(doubleSectors[p][q], data);
            for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
	            if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
		            printf("%c", data[j]);
                else
		            printf("\\%x", (unsigned char)data[j]);
	        }
            printf("\n"); 
            if(k == numBytes){
                delete [] data;
                return;
            }
        }
    }

    delete [] data;
    return;
}
